﻿using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Data.Common;
using Nectar.DAL.Common;


namespace P2PWeb.Models
{
    public class Repository<TEntities> : System.IDisposable where TEntities : ObjectContext, new()
    {
        public ObjectContext context;

        public Repository()
        {
            context = GenericRepository<TEntities>.GetModifyContext();
            context.CommandTimeout = 120;
        }

        #region ADD通用方法
        public TEntity Add<TEntity>(TEntity entity)
        where TEntity : EntityObject
        {
            try
            {
                context.CreateObjectSet<TEntity>().AddObject(entity);
                context.SaveChanges();
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return entity;

        }
        #endregion

        #region UPDATE通用方法
        public TEntity modify<TEntity>(TEntity old_data, TEntity new_data)
            where TEntity : EntityObject
        {
            try
            {
                PropertyInfo[] pis = typeof(TEntity).GetProperties();
                foreach (PropertyInfo pi in pis)
                {
                    if (((System.Reflection.MemberInfo)(pi)).Name != "ID")
                    {
                        if (pi.GetValue(old_data, null) != pi.GetValue(new_data, null) && pi.GetValue(new_data, null) != null)
                        {
                            var value1 = pi.GetValue(new_data, null);
                            pi.SetValue(old_data, value1);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                
            }
            return old_data;
        }

        /// <summary>
        /// 更新没有继承的实体
        /// </summary>
        /// <typeparam name="TEntity">实体的类型</typeparam>
        /// <param name="entity">修改后的实体</param>
        /// <returns>修改后的实体</returns>
        public TEntity Update<TEntity>(TEntity entity)
            where TEntity : EntityObject
        {
            try
            {
                context.CreateObjectSet<TEntity>().Attach(entity);
                SetAllModified<TEntity>(entity, context);
                context.SaveChanges();
            }
            catch (Exception ex)
            {
                context.SaveChanges();
            }
            return entity;
        }
        #endregion

        #region Delete通用方法

        public TEntity Delete<TEntity>(TEntity entity)
            where TEntity : EntityObject
        {
            try
            {
                PropertyInfo pi = entity.GetType().GetProperty("IsDel");
                if (pi != null)
                {
                    pi.SetValue(entity, true, null);
                }
                return Update<TEntity>(entity);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region Get通用方法
        public List<TEntity> GetListOf<TEntity>(string expression, QueryParameter[] queryParameters)
            where TEntity : EntityObject
        {
            try
            {
                ObjectParameter[] parameters = Utility.PrepareParameter(queryParameters);
                string esql = Utility.PrepareSql<TEntity>(null, expression);
                ObjectQuery<TEntity> query = context.CreateQuery<TEntity>(esql, parameters);
                List<TEntity> list = query.ToList();
                return list;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public List<TEntity> GetListOf<TEntity>(string expression, QueryParameter[] queryParameters, List<string> include)
            where TEntity : EntityObject
        {
            try
            {
                using (TEntities context = new TEntities())
                {
                    ObjectParameter[] parameters = Utility.PrepareParameter(queryParameters);
                    string esql = Utility.PrepareSql<TEntity>(null, expression);
                    if (include == null)
                        include = new List<string>();
                    ObjectQuery<TEntity> query = context.CreateQuery<TEntity>(esql, parameters);
                    for (int i = 0; i < include.Count; i++)
                    {
                        query = query.Include(include[i]);
                    }
                    List<TEntity> list = query.ToList();
                    return list;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public TEntity GetEntityByID<TEntity>(int id)
            where TEntity : EntityObject
        {
            try
            {
                ObjectParameter[] parameters = PrepareParameter(null);
                string expression = "o.ID=" + id;
                string esql = PrepareSql<TEntity>(null, expression);
                ObjectQuery<TEntity> query = context.CreateQuery<TEntity>(esql, parameters);
                List<TEntity> list = query.ToList();
                if (list.Count > 0)
                    return list.FirstOrDefault();
                else
                    return null;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

       
        public List<TEntity> GetEntityByAccountID<TEntity>(int id)
             where TEntity:EntityObject
        {
            try
            {
                ObjectParameter[] parameters = PrepareParameter(null);
                string expression = "o.AccountID=" + id;
                string esql = PrepareSql<TEntity>(null, expression);
                ObjectQuery<TEntity> query = context.CreateQuery<TEntity>(esql, parameters);
                List<TEntity> list = query.ToList();
                if (list.Count > 0)
                    return list;
                else
                    return null;    
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public TEntity GetEntityByID<TEntity>(int id, List<string> include)
            where TEntity : EntityObject
        {
            try
            {
                ObjectParameter[] parameters = PrepareParameter(null);
                string expression = "o.ID=" + id;
                string esql = PrepareSql<TEntity>(null, expression);
                if (include == null)
                    include = new List<string>();
                ObjectQuery<TEntity> query = context.CreateQuery<TEntity>(esql, parameters);
                for (int i = 0; i < include.Count; i++)
                {
                    query = query.Include(include[i]);
                }
                List<TEntity> list = query.ToList();
                if (list.Count > 0)
                    return list.FirstOrDefault();
                else
                    return null;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion



        #region IDisposable 成员

        public void Dispose()
        {
            this.context.Dispose();
        }

        #endregion

        private void SetAllModified<T>(T entity, ObjectContext context) where T : IEntityWithKey
        {
            var stateEntry = context.ObjectStateManager.GetObjectStateEntry(entity.EntityKey);
            var propertyNameList = stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(pn => pn.FieldType.Name);
            foreach (var propName in propertyNameList)
            {
                stateEntry.SetModifiedProperty(propName);
            }
        }

        private string PrepareSql<TEntity>(string property, string expression)
        {
            string esql = "";
            if (string.IsNullOrWhiteSpace(property))
                property = "value o";
            Type baseType = Utility.GetBaseType(typeof(TEntity));
            if (baseType.Name == typeof(TEntity).Name)
                esql = "select " + property + " from " + Utility.toPlural(typeof(TEntity).Name) + " as o ";
            else
                esql = "select " + property + " from OfType(" + Utility.toPlural(baseType.Name) + "," + typeof(TEntity).FullName + ") as o ";
            return PrepareExpression(esql, expression);
        }

        private ObjectParameter[] PrepareParameter(QueryParameter[] queryParameters)
        {
            ObjectParameter[] parameters;
            if (queryParameters == null)
                parameters = new ObjectParameter[0];
            else
            {
                parameters = new ObjectParameter[queryParameters.Length];
                for (int i = 0; i < queryParameters.Length; i++)
                {
                    parameters[i] = new ObjectParameter(queryParameters[i].Name, queryParameters[i].Value);
                }
            }
            return parameters;
        }

        public static string PrepareExpression(string esql, string expression)
        {
            if (string.IsNullOrEmpty(expression) || expression.Trim() == "")
                return esql + " where o.IsDel=false";
            if (expression.ToLower().TrimStart().IndexOf("order") == 0 || expression.ToLower().TrimStart().IndexOf("skip") == 0 || expression.ToLower().TrimStart().IndexOf("limit") == 0)
                esql += " where o.IsDel=false " + expression;
            else
            {
                //默认增加isdel=0的查询条件
                if (expression.IndexOf("o.IsDel") < 0)
                    esql += " where o.IsDel=false and " + expression;
                else
                    esql += " where " + expression;
            }
            return esql;
        }



    }

    public class GenericRepository<TEntities> where TEntities : ObjectContext, new()
    {
        public static TEntities GetModifyContext()
        {
            Type type = typeof(TEntities);
            Type[] typelist = new Type[1];
            typelist[0] = typeof(string);
            System.Reflection.ConstructorInfo ci = type.GetConstructor(typelist);
            object[] connstr = new object[1];
            connstr[0] = System.Configuration.ConfigurationManager.ConnectionStrings[type.Name].ConnectionString;
            return (TEntities)ci.Invoke(connstr);
        }

    }


}